from stacking.loader.processing.itemtrajectories import analyze_item_trajectories
from stacking.loader.processing.sim import simulate, InitialStocks, apply_initial_stock
from stacking.loader.processing.sim import CraneCapacity, MaxHeight
from stacking.loader.processing.cranespeed import compute_crane_speed
from stacking.loader.processing.placecoords import compute_place_coords
from stacking.loader.processing.releaseintervals import compute_release_intervals
from stacking.loader.processing.deliveryintervals import compute_delivery_intervals


def process(warehouse, items, actions, verbose=True):
    # identify reentries and create item copies as required
    items, actions = analyze_item_trajectories(items, actions, verbose=verbose)
    
    # compute initial stock in cranes and stacks and modify the warehouse's state accordingly
    initial_stock = simulate(warehouse, items, actions, [InitialStocks()])
    apply_initial_stock(warehouse, items, initial_stock)
    
    # please note that initial stocks **MUST BE COMPUTED AND APPLIED** before crane capacity and 
    # maximum stack height, as computing these without initial stocks may lead to invalid results 
    # since initial items may not be fully taken into consideration
    crane_capacity, max_height = simulate(warehouse, items, actions, [CraneCapacity(), MaxHeight()])
    crane_speed = compute_crane_speed(actions, verbose=verbose)
    # set these indicators as metadata since this is not used in the SP
    warehouse.meta.max_height = max_height
    for crane in warehouse.cranes:
        crane.meta.capacity = crane_capacity
        crane.meta.speed = crane_speed
        
    compute_place_coords(warehouse, actions)
    compute_release_intervals(items, actions, verbose=verbose)
    compute_delivery_intervals(warehouse, items, actions, verbose=verbose)
    return warehouse, items, actions
    
    
    
##def compute_stack_dimensions(actions, verbose=True):
##    """Computes the minimum and maximum width and length of items placed in each stack.
##    NOTE: This function MUST be called only AFTER COMPUTING THE INITIAL STOCKS!"""
##    # compute initial stack dimensions based on initial stocks
##    INF = sys.float_info.max
##    for stack in actions.warehouse.buffers:
##        if len(stack.meta.initial_stock) == 0:
##            stack.meta.update(min_width=+INF, 
##                              max_width=-INF, 
##                              min_length=+INF, 
##                              max_length=-INF)
##        else:
##            widths  = [item.meta.width  for item in stack.meta.initial_stock]
##            lengths = [item.meta.length for item in stack.meta.initial_stock]
##            stack.meta.update(min_width=min(widths),
##                              max_width=max(widths), 
##                              min_length=min(lengths), 
##                              max_length=max(lengths))
##    # go through the actions and update stack dimensions accordingly
##    for move in actions.actions:
##        if move.is_pickup:
##            continue
##        place = actions.warehouse.places[move.place]
##        if not place.is_buffer:
##            continue
##        items   = [actions.warehouse.items[id] for id in move.items]
##        widths  = [item.meta.width  for item in items]
##        lengths = [item.meta.length for item in items]
##        place.meta.update(min_width=min(place.meta.min_width, min(widths)),
##                          max_width=max(place.meta.max_width, max(widths)), 
##                          min_length=min(place.meta.min_length, min(lengths)), 
##                          max_length=max(place.meta.max_length, max(lengths)))
##    # validate results: no stack may have min or max dimensions equal to +/-INF
##    for stack in actions.warehouse.buffers:
##        if verbose:
##            print "%r: width in [%s, %s] length in [%s, %s]" % \
##                (stack, stack.meta.min_width, stack.meta.max_width,
##                 stack.meta.min_length, stack.meta.max_length),
##            if stack.meta.min_width  == +INF \
##            or stack.meta.max_width  == -INF \
##            or stack.meta.min_length == +INF \
##            or stack.meta.max_length == -INF:
##                print "ERROR"
##            else:
##                print "okay"
##                
##                
##def compute_item_stack_compatibility(actions, verbose=True):
##    """For each item, create a list of stack ids where the item can be placed.
##    NOTE: Requires stack dimensions to be computed first."""
##    for item in actions.warehouse.items.itervalues():
##        item.meta.compatible_buffers = []
##        for stack in actions.warehouse.buffers:
##            if  stack.meta.min_width  <= item.meta.width  <= stack.meta.max_width \
##            and stack.meta.min_length <= item.meta.length <= stack.meta.max_length:
##                item.meta.compatible_buffers.append(stack)
##        if verbose:
##            print "%s: %d compatible buffers" % (item, len(item.meta.compatible_buffers))
            
            
